﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Data;

using MyLibrary.ThreeLayer.DataAccess.MetaData;
using MyLibrary.ThreeLayer.DataAccess.Interface;
using MyLibrary.ThreeLayer.DataAccess.QueryLanguage;
using MyLibrary.ThreeLayer.DataAccess.Enum;
using MyLibrary.ThreeLayer.DataAccess.CustomException;

namespace MyLibrary.ThreeLayer.DataAccess.Provider.CommandBuilder
{
    internal class CommandBuilder<T> : ISQLCommandBuilder<T> where T : class, new()
    {
        /// <summary>
        /// 添加筛选条件
        /// </summary>
        /// <param name="provider">数据适配器</param>
        /// <param name="command">构建的SQLCommand</param>
        /// <param name="expression">条件表达式</param>
        private void AppendWhereCondition(IDbProvider provider, IDbCommand command, ConditionExpression expression)
        {
            IQueryExpressionConvertor queryExpressionConvertor = QueryExpressionConvertFactory.GetQueryExpressionConvertor(provider.ProviderType);
            IDictionary<string, object> paramList = new Dictionary<string, object>();
            string str = queryExpressionConvertor.Parser(expression, paramList);
            if (!String.IsNullOrEmpty(str))
            {
                command.CommandText = command.CommandText + " Where " + str;
                command.CommandType = CommandType.Text;
                foreach (KeyValuePair<string, object> pair in paramList)
                {
                    IDataParameter parameter = provider.CreateParameter();
                    parameter.ParameterName = pair.Key;
                    parameter.Value = pair.Value;
                    command.Parameters.Add(parameter);
                }
            }
        }

        /// <summary>
        /// 根据设定的实体关联关系,删除所有有关的记录
        /// </summary>
        /// <param name="deletedType">删除实体</param>
        /// <param name="refType">关联实体类型</param>
        /// <param name="inSubSql">查询子句</param>
        /// <param name="sqlList">删除语句集合</param>
        private void BuildDeleteCommandWithRefTypeValue(Type deletedType, Type refType, string inSubSql, IList<string> sqlList)
        {
            if (deletedType.Equals(refType))
                throw new NotSupportedException("不支持使用引用类型本身");

            MappingCollection collection = MetaDataFactory.GetMappingCollection(deletedType);
            MappingField refField = MetaDataFactory.GetMappingField(refType);

            if (refField == null)
                throw new MappingException(deletedType.FullName + "不存在引用对象", deletedType);
            string strCommand = "Where [" + refField.ColumnName + "] In (" + inSubSql + ")";
            sqlList.Add("Delete From [" + collection._TableName + "] " + strCommand);
            IList<Relation> childRelation = collection.GetChildRelation();
            if (childRelation.Count > 0)
            {
                IList<MappingField> keyFields = collection.GetKeyFields();
                if (keyFields.Count == 0)
                    throw new MappingException(deletedType.FullName + "不存在主键", deletedType);
                if (keyFields.Count > 1)
                    throw new MappingException(deletedType.FullName + "的主键超过一个,无法进行关联删除", deletedType);

                string strRefCommand = "Select " + keyFields[0].ColumnName + strCommand;
                foreach (Relation relation in childRelation)
                    this.BuildDeleteCommandWithRefTypeValue(relation._RelationTarget, deletedType, strRefCommand, sqlList);
            }
        }

        /// <summary>
        /// 返回不同关联类型的关键字
        /// </summary>
        /// <param name="jType">关联类型</param>
        /// <returns>关联关键字</returns>
        private string GetJoinSQL(JoinType jType)
        {
            switch (jType)
            {
                case JoinType.LeftJoin:
                    return "Left";
                case JoinType.RightJoin:
                    return "Right";
                case JoinType.InnerJoin:
                    return "Inner";
            }
            return String.Empty;
        }

        #region ISQLCommandBuilder<T> Members

        /// <summary>
        /// 建立删除语句[删除所有的记录]
        /// </summary>
        public virtual IDbCommand BuildDeleteCommand(IDbProvider provider, MappingCollection collection)
        {
            IDbCommand command = provider.CreateCommand();
            command.CommandText = "Delete From [" + collection._TableName + "] ";
            return command;
        }

        /// <summary>
        /// 建立删除语句[自定义的删除条件]
        /// </summary>
        public virtual IDbCommand BuildDeleteCommand(IDbProvider provider, MappingCollection collection, ConditionExpression expression)
        {
            IDbCommand command = provider.CreateCommand();
            command.CommandText = "Delete From [" + collection._TableName + "] ";
            this.AppendWhereCondition(provider, command, expression);
            return command;
        }

        /// <summary>
        /// 建立删除语句[根据设定的主键]
        /// </summary>
        public virtual IDbCommand BuildDeleteCommand(IDbProvider provider, MappingCollection collection, T entity)
        {
            IList<MappingField> keyFields = collection.GetKeyFields();
            if (keyFields.Count == 0)
                throw new ArgumentException(typeof(T).FullName + " 中不存在主键");

            IDbCommand command = provider.CreateCommand();
            string str = "Delete From [" + collection._TableName + "] Where ";
            string conditions = String.Empty;
            foreach (MappingField field in keyFields)
            {
                if (conditions != String.Empty)
                    conditions += " And ";
                conditions = conditions + "[" + field.ColumnName + "]=@" + field.ColumnName;
                IDataParameter paramer = provider.CreateParameter();
                paramer.Value = field.GetValue(entity);
                command.Parameters.Add(paramer);
            }

            str = str + conditions;
            command.CommandText = str;
            return command;
        }

        /// <summary>
        /// 建立删除语句[给定删除条件和值]
        /// </summary>
        public virtual IDbCommand BuildDeleteCommand(IDbProvider provider, MappingCollection collection, MappingField key, object keyValue)
        {
            IDbCommand command = provider.CreateCommand();
            string strCommand = "Delete From [" + collection._TableName + "] Where ";
            string conditions = strCommand + "[" + key.ColumnName + "]=@" + key.ColumnName;

            IDataParameter parameter = provider.CreateParameter();
            parameter.ParameterName = "@" + key.ColumnName;
            parameter.Value = keyValue;
            command.Parameters.Add(parameter);
            command.CommandText = strCommand;
            return command;
        }

        /// <summary>
        /// 建立删除语句[给定两个相关联删除条件和值]
        /// </summary>
        public virtual IDbCommand BuildDeleteCommand(IDbProvider provider, object entity1, object entity2)
        {
            IDataParameter parameter;

            MappingCollection mappingCollection1 = MetaDataFactory.GetMappingCollection(entity1.GetType());
            MappingCollection mappingCollection2 = MetaDataFactory.GetMappingCollection(entity2.GetType());
            IList<MappingCollection> keyFields1 = mappingCollection1.GetKeyFields();
            IList<MappingCollection> keyFields2 = mappingCollection2.GetKeyFields();

            if (keyFields1.Count == 0)
                throw new ArgumentException("在" + entity1.GetType().ToString() + "不存在主键");
            if (keyFields2.Count == 0)
                throw new ArgumentException("在" + entity2.GetType().ToString() + "不存在主键");

            Relation relationByModeAndType = mappingCollection1.GetRelationByModeAndType(RelationType.ManyToMany, entity2.GetType());
            if (relationByModeAndType == null)
                relationByModeAndType = mappingCollection2.GetRelationByModeAndType(RelationType.ManyToMany, entity1.GetType());
            if (relationByModeAndType == null)
                throw new RelationException("传入的两个参数实体直接不存在关联关系");

            string strCommand = "Delete From [" + relationByModeAndType._RelationTable + "] Where ";
            IDbCommand command = provider.CreateCommand();

            //得到第一个实体的条件
            string condition = String.Empty;
            foreach (MappingField field in keyFields1)
            {
                if (condition != String.Empty)
                    condition += " And ";
                condition = condition + "[" + field.ColumnName + "]=@" + field.ColumnName;
                parameter = provider.CreateParameter();
                parameter.ParameterName = "@" + field.ColumnName;
                parameter.Value = field.GetValue(entity1);
                command.Parameters.Add(parameter);
            }

            foreach (MappingField field in keyFields2)
            {
                if (condition != String.Empty)
                    condition += " And ";
                condition = condition + "[" + field.ColumnName + "]=@" + field.ColumnName;
                parameter = provider.CreateParameter();
                parameter.ParameterName = "@" + field.ColumnName;
                parameter.Value = field.GetValue(entity2);
                command.Parameters.Add(parameter);
            }

            strCommand = strCommand + condition;
            command.CommandText = strCommand;
            return command;
        }

        public IDbCommand BuildDeleteCommand(IDbProvider provider, MappingCollection collection, object keyValue)
        {
            throw new NotImplementedException();
        }

        public virtual IDbCommand BuildInsertCommand(IDbProvider provider, MappingCollection collection, T entity)
        {
            IList<MappingField> insertFields = collection.GetInsertFields();
            if (insertFields.Count == 0)
                throw new MappingException("不存在插入字段", typeof(T));

            string strCommand = "Insert Into [" + collection._TableName + "] ";
            string tmpString1 = String.Empty,tmpString2 = String.Empty;

            IDbCommand command = provider.CreateCommand();
            foreach (MappingField field in insertFields)
            {
                if (tmpString1 != String.Empty)
                {
                    tmpString1 += ",";
                    tmpString2 += ",";
                }

                tmpString1 += "[" + field.ColumnName + "]";
                tmpString2 += "@" + field.ColumnName;

                IDataParameter parameter = provider.CreateParameter();
                parameter.ParameterName = "@" + field.ColumnName;
                parameter.Value = field.GetValue(entity);
                command.Parameters.Add(parameter);
            }

            strCommand = strCommand + "(" + tmpString1 + ") Values (" + tmpString2 + ")";
            command.CommandText = strCommand;
            return command;
        }

        public IDbCommand BuildInsertCommand(IDbProvider provider, object entity1, object entity2)
        {
            throw new NotImplementedException();
        }

        public IDbCommand BuildJoinQueryCommand(IDbProvider provider, MappingCollection collection, ConditionExpression expression)
        {
            throw new NotImplementedException();
        }

        public IDbCommand BuildQueryCommand(IDbProvider provider, MappingCollection collection, ConditionExpression expression)
        {
            throw new NotImplementedException();
        }

        public IDbCommand BuildQueryCommand(IDbProvider provider, MappingCollection collection)
        {
            throw new NotImplementedException();
        }

        public IDbCommand BuildQueryCommand(IDbProvider provider, MappingCollection collection, MappingField selectKey, object keyValue)
        {
            throw new NotImplementedException();
        }

        public IDbCommand BuildQueryCommand(IDbProvider provider, MappingCollection collection, IList<MappingField> keys, T entity)
        {
            throw new NotImplementedException();
        }

        public IDbCommand BuildQueryCommand(IDbProvider provider, MappingCollection collection, T entity)
        {
            throw new NotImplementedException();
        }

        public IDbCommand BuildQueryCommand(IDbProvider provider, QueryExpression expression)
        {
            throw new NotImplementedException();
        }

        public IDbCommand BuildUpdateCommand(IDbProvider provider, MappingCollection collection, T entity)
        {
            throw new NotImplementedException();
        }

        public IDbCommand BuildUpdateCommand(IDbProvider provider, MappingCollection collection, T entity, ConditionExpression expression)
        {
            throw new NotImplementedException();
        }

        public IDbCommand BuildUpdateCommand(IDbProvider provider, MappingCollection collection, T entity, ConditionExpression expression, IQueryField[] fields)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// 组件更新语句
        /// </summary>
        /// <param name="provider">数据库适配器</param>
        /// <param name="collection">需要更新的表</param>
        /// <param name="entity">更新实体</param>
        /// <param name="expression">条件表达式</param>
        /// <param name="updateFields">更新域</param>
        /// <returns>更新语句</returns>
        public IDbCommand BuildUpdateCommand(IDbProvider provider, MappingCollection collection, T entity, ConditionExpression expression, string updateFields)
        {
            if (collection.GetUpdateFields().Count == 0)
                throw new MappingException("没有需要更新的字段", typeof(T));
            if (String.IsNullOrEmpty(updateFields))
                throw new ArgumentNullException("updateFields", "更新域不能为空");

            IDbCommand command = provider.CreateCommand();
            string strCommand = "Update [" + collection._TableName + "] Set ";
            string tmpString = String.Empty;
            string[] strArray = updateFields.Split(',');

            foreach (string str in strArray)
            {
                MappingField field = collection.GetField(str);
                if (field == null)
                    throw new ArgumentNullException("指定的域" + str + "不存在");
                if (tmpString == String.Empty)
                    tmpString += ",";
                tmpString = tmpString + "[" + field.ColumnName + "]=@" + field.ColumnName;
                IDataParameter parameter = provider.CreateParameter();
                parameter.ParameterName = "@" + field.ColumnName;
                parameter.Value = field.GetValue(entity);
                command.Parameters.Add(parameter);
            }

            command.CommandText = strCommand + tmpString;
            this.AppendWhereCondition(provider, command, expression);
            return command;
        }

        #endregion
    }
}
